import { baseURL } from 'boot/env.js'
import { i18n } from 'boot/i18n'
import { debounce, Dialog, extend, Notify, Screen } from 'quasar'
import axios from 'src/axios'

const map = {
  '[object Boolean]': 'boolean',
  '[object Number]': 'number',
  '[object String]': 'string',
  '[object Function]': 'function',
  '[object Array]': 'array',
  '[object Date]': 'date',
  '[object RegExp]': 'regExp',
  '[object Undefined]': 'undefined',
  '[object Null]': 'null',
  '[object Object]': 'object'
}

/**
 * 获得参数类型
 */
export function getTypeof (arg) {
  return arg instanceof Element ? 'element' : map[toString.call(arg)]
}

/**
 * 参数 深拷贝
 */
export function deepClone (arg, deep = true) {
  const type = getTypeof(arg)
  if (type === 'array') return deep ? extend(true, [], arg) : [...arg]
  if (type === 'object') return deep ? extend(true, {}, arg) : { ...arg }
  return arg
}

/**
 * 判断参数是否为空
 */
export function isEmpty (arg) {
  const type = getTypeof(arg)
  if (['undefined', 'null'].includes(type)) return true
  if (type === 'string' && arg === '') return true
  if (type === 'array') return !arg.length
  if (type === 'object') return !Object.keys(arg).length
  return false
}

export const connectEventSource = (url = '', callback, done) => {
  // 创建EventSource对象
  const source = new EventSource(`${baseURL}${url}`)
  // 监听连接成功
  source.onopen = () => done && done()
  // 监听服务器端发送的消息
  source.onmessage = ({ data = '' }) => callback && callback(data && isJSON(data) && JSON.parse(data))
  return source
}

/**
 * 格式化数字，保留n位小数
 * number: Number 需要格式化点数字
 * decimal: 保留小数位数 默认为3
 */
export const formatNumber = (number = 0, decimal = 3) => {
  const n = Math.pow(10, decimal)
  return Math.round(Number(number || 0) * n) / n
}

export const formatImgWH = (img, IMGSIZE = 2) => {
  let { width = 1, height = 1 } = img ?? {}
  if (width > height) {
    width = formatNumber(width / height * IMGSIZE)
    height = IMGSIZE
  } else {
    height = formatNumber(height / width * IMGSIZE)
    width = IMGSIZE
  }
  return { width, height }
}

export const arrayToMap = (arr = [], key = '') => {
  if (getTypeof(arr) !== 'array') return new Map()
  return (arr || []).reduce((acc, item, index) => acc.set(item, `${key}${index}`), new Map())
}
export const mapToArray = (map, key = 'keys') => {
  map = map || new Map()
  return typeof map[key] === 'function' ? [...map[key]()] : []
}

export const replaceFromRight = (str = '', target = '', replacement = '') => {
  // 构造匹配最后一个 target 的正则
  return str.replace(new RegExp(`${target}(?=[^${target}]*$)`), replacement)
}

// 分帧加载
export const loadByFrame = (sourceData = [], batchSize = 1000, batchLoaded, loadCompleted) => {
  let currentIndex = 0

  const loadNextFrame = async () => {
    const batch = sourceData.slice(currentIndex, currentIndex + batchSize)
    batchLoaded && await batchLoaded(batch)
    currentIndex += batchSize
    if (currentIndex < sourceData.length) requestAnimationFrame(loadNextFrame)
    else loadCompleted && await loadCompleted()
  }

  requestAnimationFrame(loadNextFrame)
}
// 分帧异步加载
export const loadByFrameAsync = (sourceData, batchSize = 1000, batchLoaded, loadCompleted) => {
  return new Promise(resolve => {
    let currentIndex = 0

    const loadNextFrame = async () => {
      const batch = sourceData.slice(currentIndex, currentIndex + batchSize)
      batchLoaded && await batchLoaded(batch)
      currentIndex += batchSize
      if (currentIndex < sourceData.length) requestAnimationFrame(loadNextFrame)
      else resolve(loadCompleted && await loadCompleted())
    }

    requestAnimationFrame(loadNextFrame)
  })
}
const btnAttrs = { class: 'col-6 no-margin', square: true, padding: '.6rem' }
let WarnModal = null
const WarnModalClose = debounce(() => WarnModal && WarnModal.hide(), 5000)
export const createWarnTips = ({ title = '', msg = '', okLabel, cancelLabel, persistent = false, prompt },
  onOk = void 0, onCancel = void 0) => {
  const opts = { title: '', prompt, persistent, html: true, style: 'width: auto;', class: 'q-dialog-tips' }
  opts.message = `<div class="row items-center text-subtitle1 text-bold">
                    <i class="mdi mdi-information-variant-circle-outline text-amber text-h4"></i>
                    <span class="q-pl-md">${title || ''}</span>
                  </div>${msg || ''}`
  opts.ok = { color: 'primary', unelevated: true, label: okLabel || i18n.t('Confirm'), ...btnAttrs }
  opts.cancel = { flat: true, label: cancelLabel || i18n.t('Cancel'), textColor: 'negative', ...btnAttrs }
  if (WarnModal) return WarnModal.update(opts) && WarnModalClose()
  WarnModal = Dialog.create(opts).onOk(val => onOk && onOk(val)).onCancel(() => onCancel && onCancel())
    .onDismiss(() => (WarnModal = null))

  WarnModalClose()
  return WarnModal
}

let ErrorModal = null
const ErrorModalClose = debounce(() => ErrorModal && ErrorModal.hide(), 8000)
export const createErrorTips = ({ title = '', msg = '', okLabel, persistent = false }, onOk = void 0) => {
  const opts = { title: '', persistent, html: true, style: 'width: auto;', class: 'q-dialog-tips' }
  opts.message = `<div class="row items-center text-subtitle1 text-bold">
                    <i class="mdi mdi-close-circle-outline text-negative text-h4"></i>
                    <span class="q-pl-md">${title || ''}</span>
                  </div>${msg || ''}`
  opts.ok =
    { color: 'primary', label: okLabel || i18n.t('Confirm'), ...btnAttrs, class: 'full-width', unelevated: true }
  if (ErrorModal) return ErrorModal.update(opts) && ErrorModalClose()
  ErrorModal = Dialog.create(opts).onOk(val => onOk && onOk(val)).onDismiss(() => (ErrorModal = null))

  ErrorModalClose()
  return ErrorModal
}

let SuccessModal = null
const SuccessModalClose = debounce(() => SuccessModal && SuccessModal.hide(), 3000)

export const createSuccessTips = ({ title = '', msg = '', okLabel, persistent = false }, onOk = void 0) => {
  const opts = { title: '', persistent, html: true, style: 'width: auto;', class: 'q-dialog-tips' }
  opts.message = `<div class="row items-center text-subtitle1 text-bold">
                    <i class="mdi mdi-checkbox-multiple-marked-circle-outline text-positive text-h4"></i>
                    <span class="q-pl-md">${title || ''}</span>
                  </div>${msg || ''}`
  if (SuccessModal) return SuccessModal.update(opts) && SuccessModalClose()
  opts.ok =
    { color: 'primary', label: okLabel || i18n.t('Confirm'), ...btnAttrs, class: 'full-width', unelevated: true }
  SuccessModal = Dialog.create(opts).onOk(val => onOk && onOk(val)).onDismiss(() => (SuccessModal = null))

  SuccessModalClose()
  return SuccessModal
}

/**
 * 格式化秒为时分秒
 */
export const formatSecondToHMS = (second = 0, format = {}) => {
  const { H = 'h', M = 'm', S = 's' } = format
  if (!second || second < 60) return `${second}${S}`
  const numH = Math.floor(second / 3600)
  const numM = Math.floor(second % 3600 / 60)
  const numS = Math.floor(second % 3600 % 60)
  return `${numH ? numH + H : ''}${numM ? numM + M : ''}${numS ? numS + S : ''}`
}

/**
 * 版本号比较大小
 * v1: 版本号1
 * v2: 版本号2
 */
export const versionCompare = (v1 = '', v2 = '') => {
  v1 = v1.toUpperCase().replace('V', '')
  v2 = v2.toUpperCase().replace('V', '')
  const v1List = v1.split('.')
  const v2List = v2.split('.')
  for (let i = 0; i < Math.max(v1List.length, v2List.length); i++) {
    const v1Num = parseInt(v1List[i] || 0)
    const v2Num = parseInt(v2List[i] || 0)
    if (v1Num > v2Num) return -1
    if (v1Num < v2Num) return 1
  }
  return 0
}

/**
 * 动态适配Echarts文字大小
 * fontSize: 1920px宽度下的文字大小
 */
export const adaptFontSize = (fontSize = 16) => {
  if (fontSize) return fontSize
  let scale = Screen.width / 1920
  if (scale < 0.8) scale = 0.75
  else if (scale > 1.2) scale = 1.35
  return scale * fontSize
}

/**
 * 根据value匹配color
 * value: [String, Number, Boolean]
 * options: Array
 * props: Object
 * defaultColor: String
 */
export const getColorByValue = (value, options, props = {}, defaultColor) => {
  if (!value || !options || !options.length) return defaultColor
  const colorKey = props.color || 'color', valueKey = props.value || 'value'
  const findColor = options.find(el => el[valueKey] === value) || {}
  return findColor[colorKey] ?? defaultColor
}

/**
 * 根据value匹配label
 * value: [String, Number, Boolean]
 * options: Array
 * props: Object
 * defaultValue: String
 */
export const getLabelByValue = (value, options, props = {}, defaultValue = '') => {
  if (!value || !options || !options.length) return value || defaultValue
  const labelKey = props.label || 'label', valueKey = props.value || 'value'
  const findLabel = options.find(el => el[valueKey] === value) || {}
  return findLabel[labelKey] ?? (value || defaultValue)
}

/**
 * 根据values匹配labels
 * value: String
 * options: Array
 * props: Object
 * defaultValue: String
 */
export const getLabelByValues = (value = '', options, props = {}, defaultValue = '') => {
  if (!value || !options || !options.length) return value || defaultValue
  const labelKey = props.label || 'label', valueKey = props.value || 'value', formatValue = value.split(',')
  const filterLabel = options.filter(el => formatValue.includes(el[valueKey])) || []
  if (!filterLabel.length) return value || defaultValue
  return filterLabel.map(el => el[labelKey]).join(', ')
}

/**
 * 树形转线形
 */
export const treeToList = (tree, filed = 'children') => {
  const list = []
  for (const el of tree) {
    if (el.hide) continue
    list.push(el)
    if (el[filed]?.length) list.push.apply(list, treeToList(el[filed]))
  }
  return list
}

/**
 * 线性转树形
 */
export const listToTree = (list = [], { key = 'children', id = 'id', parentId = 'parentId' }, filterFunc = void 0) => {
  const returnTree = [], map = new Map()
  for (const el of list) map.set(el[id], el)
  for (const el of list) {
    if (filterFunc && filterFunc(el)) continue
    const { [parentId]: dynamicParentId } = el
    const node = map.get(dynamicParentId)
    if (!node) returnTree.push(el)
    else {
      if (!node[key]) node[key] = []
      node[key].push(el)
    }
  }
  return returnTree
}

// 正则--判断是否数字
const REG = /(^[-0-9][0-9]*(.[0-9]+)?)$/

/**
 * 字符串比较大小
 */
export const compare = (a = '', b = '') => {
  a = String(a).replace('%', '')
  b = String(b).replace('%', '')
  if (REG.test(a) && REG.test(b)) return Number(a) > Number(b)
  return a > b
}

/**
 * 判断参数是否是百分数
 * arg: 需要判断的参数
 */
export function isPercentage (arg) {
  return /^\d+(\.\d+)?%$/.test(arg)
}

/**
 * 判断参数是否是安全数字
 * arg: 需要判断的参数
 */
export const isSafeNumeric = (arg) => {
  if (typeof arg === 'number') return Number.isFinite(arg) && Math.abs(arg) <= Number.MAX_SAFE_INTEGER
  return typeof arg === 'string' && arg.trim() ? !isNaN(Number(arg)) && isSafeNumeric(Number(arg)) : false
}

/**
 * 判断参数是否为JSON
 * arg: 需要判断的参数
 */
export function isJSON (arg) {
  if (!arg) return true
  if (typeof arg !== 'string') return false
  try {
    const parseStr = JSON.parse(arg)
    return !!(parseStr && typeof parseStr === 'object')
    // eslint-disable-next-line no-unused-vars
  } catch (e) {
    return false
  }
}

/**
 * 判断参数是否为字母数字格式
 * arg: 需要判断的参数
 */
export function isAZNUM (arg) {
  return arg ? /^[\da-zA-Z]*$/.test(arg) : true
}

/**
 * 判断参数是否为非中文格式
 * arg: 需要判断的参数
 */
export function isNonChinese (arg) {
  return arg ? !/[\u4e00-\u9fa5]/.test(arg) : true
}

/**
 * 判断参数是否为字母格式
 * arg: 需要判断的参数
 */
export function isAZ (arg) {
  return arg ? /^[a-zA-Z]*$/.test(arg) : true
}

/**
 * 正则验证电话
 */
export function valiPhone (arg) {
  return arg ? /^1[3-9]\d{9}$/.test(arg) : true
}

/**
 * 正则验证邮箱
 */
export function valiEmail (arg) {
  return arg ? /^(\w-*\.*)+@(\w-?)+(\.\w{2,})+$/.test(arg) : true
}

/**
 * 对象数组去重
 */
export function unique (list, key = 'id', deep = false) {
  const res = new Map()
  if (deep) list = deepClone(list)
  list.reverse()
  list = list.filter(el => !res.has(el[key]) && res.set(el[key], 1))
  return list.reverse()
}

/**
 * 确认弹窗 统一封装
 */
export function createDialog ({ title, msg = '', okLabel, cancelLabel, prompt, cancelBtn = true },
  onOk = void 0, onCancel = void 0) {
  const opts = { title: title || i18n.t('Hint'), message: msg, prompt, persistent: true, class: 'q-dialog-confirm' }

  if (cancelBtn) {
    opts.ok = { color: '#FFF', label: okLabel || i18n.t('Confirm'), textColor: 'primary' }
    opts.cancel = { color: '#FFF', label: cancelLabel || i18n.t('Cancel'), textColor: 'negative' }
  } else {
    opts.ok = { color: '#FFF', label: okLabel || i18n.t('Confirm'), textColor: 'primary', class: 'full-width' }
  }

  Dialog.create(opts).onOk(val => onOk && onOk(val)).onCancel(() => onCancel && onCancel())
}

export const createSuccessMsg = (msg = i18n.t('LogCore.OperationTip')) => {
  Notify.create({ type: 'positive', message: msg })
}

/**
 * 打开新窗口
 * url: 地址
 * title: 窗口标题
 * w: 窗口宽度
 * h: 窗口高度
 */
export function openWindow (url, title, w, h) {
  const dualScreenLeft = window.screenLeft !== undefined ? window.screenLeft : screen.left
  const dualScreenTop = window.screenTop !== undefined ? window.screenTop : screen.top

  const width = window.innerWidth ? window.innerWidth : document.documentElement.clientWidth
    ? document.documentElement.clientWidth : screen.width
  const height = window.innerHeight ? window.innerHeight : document.documentElement.clientHeight
    ? document.documentElement.clientHeight : screen.height
  const left = ((width / 2) - (w / 2)) + dualScreenLeft, top = ((height / 2) - (h / 2)) + dualScreenTop
  const feature = 'toolbar=no, location=no, directories=yes, status=no, menubar=no, scrollbars=no, resizable=no, copyhistory=no'
  const newWindow = window.open(url, title, `${feature}, width=${w}, height=${h}, top=${top}, left=${left}`)
  if (window.focus) newWindow.focus()
  return newWindow
}

/**
 * 根据URL获取指定参数
 * url: 地址
 * key: 字段名
 */
export function getAssignParamFromUrl (url = '', key = '') {
  const list = url.match(new RegExp('[?&]' + key + '=([^&]+)'))
  return list && list[1] ? list[1] : void 0
}

export function isInIframe () {
  try {
    return window.self !== window.top
    // eslint-disable-next-line no-unused-vars
  } catch (e) {
    return true
  }
}

/**
 * 文件流下载
 * @param {Object} arg {
 * dicMethod: 请求方式
 * dicUrl: 接口地址
 * dicQuery：参数
 * type: 文件类型
 * fileName: 文件名
 * done: done()  方法
 *}
 */
export function exportedStreamFile (arg) {
  const params = {
    method: arg.dicMethod || 'post', url: arg.dicUrl, data: arg.dicQuery || {}, responseType: 'blob'
  }

  axios(params).then(res => {
    if (res.data.type === 'application/json') {
      const reader = new FileReader()
      reader.readAsText(res.data, 'utf-8')
      reader.onload = function () {
        const { message } = JSON.parse(this.result || '{')
        Notify.create({ type: 'negative', message })
        if (typeof arg.done === 'function') arg.done()
      }
      return
    }
    const aLink = document.createElement('a')
    const blob = new Blob([res.data], {
      type: arg.type || 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
    })
    // 从response的headers中获取filename, 后端response.setHeader("Content-disposition", "attachment; filename=xxxx.docx")
    // 设置的文件名;
    const patt = /filename=([^;]+\.[^\\.;]+);*/
    const contentDisposition = decodeURI(res.headers['content-disposition'])
    const result = patt.exec(contentDisposition) || []
    let fileName = arg.fileName || result[1] || i18n.t('Unnamed')

    fileName = fileName.replace(/"/g, '')
    aLink.href = URL.createObjectURL(blob)
    aLink.setAttribute('download', fileName) // 设置下载文件名称
    document.body.appendChild(aLink)
    aLink.click()
    document.body.appendChild(aLink)
    if (typeof arg.done === 'function') arg.done()
  }).catch(() => {
    if (typeof arg.done === 'function') arg.done()
  })
}